<meta charset="utf-8" lang="kotlin">

# Adding Quick Fixes

## Introduction

When your detector reports an incident, it can also provide one or more
“quick fixes”, which are actions the users can invoke in the IDE (or,
for safe fixes, in batch mode) to address the reported incident.

For example, if the lint check reports an unused resource, a quick fix
could offer to remove the unused resource.

In some cases, quick fixes can take partial steps towards fixing the
problem, but not fully. For example, the accessibility lint check which
makes sure that for images you set a content description, the quickfix
can offer to add it -- but obviously it doesn't know what description
to put. In that case, the lint fix will go ahead and add the attribute
declaration with the correct namespace and attribute name, but will
leave the value up to the user (so it uses a special quick fix provided
by lint to place a TODO marker as the value, along with selecting just
that TODO string such that the user can type to replace without having
to manually delete the TODO string first.)

## The LintFix builder class

The class in lint which represents a quick fix is `LintFix`.

Note that `LintFix` is **not** a class you can subclass and then for
example implement your own arbitrary code in something like a
`perform()` method.

Instead, `LintFix` has a number of builders where you *describe* the
action that you would like the quickfix to take. Then, lint will offer
that quickfix in the IDE, and when the user invokes it, lint runs its
own implementation of the various descriptors.

The historical reason for this is that many of the quickfixes in lint
depended on machinery in the IDE (such as code and import cleanup after
an edit operation) that isn't available in lint itself, along with
other concepts that only make sense in the IDE, such as moving the
caret, opening files, selecting text, and so on.

More recently, this is also used to persist quickfixes properly for
later reuse; this is required for [partial
analysis](partial-analysis.md.html).

## Creating a LintFix

Lint fixes use a “fluent API”; you first construct a `LintFix`, and on
that method you call various available type methods, which will then
further direct you to the allowed options.

For example, to create a lint fix to set an XML attribute of a given
name to “true”, use something like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin
LintFix fix = fix().set(null, "singleLine", "true").build()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here the `fix()` method is provided by the `Detector` super class, but
that's just a utility method for `LintFix.fix()` (or in older versions,
`LintFix.create()`).

There are a number of additional, common methods you can set on
the `fix()` object:

* `name`: Sets the description of the lint fix. This should be brief;
  it's in the quickfix popup shown to the user.

* `sharedName`: This sets the “shared” or “family” name: all fixes in
  the file will with the same name can be applied in a single
  invocation by the user. For example, if you register 500 “Remove
  unused import” quickfixes in a file, you don't want to force the user
  to have to invoke each and every one. By setting the shared name, the
  user will be offered to **Fix All *$family name* problems in the
  current file**, which they can then perform to have all 500
  individual fixes applied in one go.

* `autoFix`: If you get a lint report and you notice there are a lot of
  incidents that lint can fix automatically, you don't want to have to
  go and open each and every file and all the fixes in the file.
  Therefore, lint can apply the fixes in batch mode; the Gradle
  integration has a `lintFix` target to perform this, and the `lint`
  command has an `--apply-suggestions` option.

  However, many quick fixes require user intervention. Not just the
  ones where the user has to choose among alternatives, and not just
  the ones where the quick fix inserts a placeholder value like TODO.
  Take for example lint's built-in check which requires overrides of a
  method annotated with `@CallSuper` to invoke `super.` on the
  overridden method. Where should we insert the call -- at the
  beginning? At the end?

  Therefore, lint has the `autoFix` property you can set on a quickfix.
  This indicates that this fix is “safe” and can be performed in batch
  mode. When the `lintFix` target runs, it will only apply fixes marked
  safe in this way.

## Available Fixes

The current set of available quick fix types are:

* `fix().replace`: String replacements. This is the most general
  mechanism, and allows you to perform arbitrary edits to the source
  code. In addition to the obvious “replace old string with new”, the
  old string can use a different location range than the incident
  range, you can match with regular expressions (and perform
  replacements on a specific group within the regular expression), and
  so on.

  This fix is also the most straightforward way to **delete** text.

  It offers some useful cleanup operations:

  - Source code cleanup, which will run the IDE's code formatter on the
    modified source code range. This will apply the user's code
    preferences, such as whether there should be a space between a cast
    and the expression, and so on.

  - Import cleanup. That means that if you are referencing a new type,
    you don't have to worry about checking whether it is imported and
    if not adding an import statement; you can simply write your string
    replacements using the fully qualified names, and then tag the
    quickfix with the import cleanup option, and when the quickfix is
    performed the import will be added if necessary and all the fully
    qualified references replaced with simple names. And this will also
    correctly handle the scenario where the symbols cannot be replaced
    with simple names because there is a conflicting import of the same
    name from a different package.

    Normally, you should write your replacement source code using fully
    qualified names, and then apply `shortenNames` to the quickfix to
    tell lint to replace fully qualified names with imports; don't try
    to write your quickfix to also add the import statements on its
    own. There's a possibility that a given name cannot be imported
    because it's already importing the same name for a different
    namespace. When using fully qualified names, lint will specifically
    handle this.

    In some cases you cannot use fully qualified names in the code
    snippet; this is the case with Kotlin extension functions for
    example. For that scenario, the replacement quickfix has an
    `imports` property you can use to specify methods (and classes and
    fields) to import at the same time.

* `fix().annotate`: Annotating an element. This will add (or optionally
  replace) an annotation on a source element such as a method. It will
  also handle import management.

* `fix().set`: Add XML attributes. This will insert an attribute into
  the given element, applying the user's code style preferences for
  where to insert the attribute. (In Android XML for example there's a
  specific sorting convention which is generally alphabetical, except
  layout params go before other attributes, and width goes before
  height.)

  You can either set the value to something specific, or place the
  caret inside the newly created empty attribute value, or set it
  to TODO and select that text for easy type-to-replace.

!!! Tip
   If you use the `todo()` quickfix, it's a good idea to special case
   your lint check to deliberately not accept “TODO” as a valid value.
   For example, for lint's accessibility check which makes sure you set
   a content description, it will complain both when you haven't set
   the content description attribute, **and** if the text is set to
   “TODO”. That way, if the user applies the quickfix, which creates
   the attribute in the right place and moves the focus to the right
   place, the editor is still showing a warning that the content
   description should be set.

* `fix().unset`: Remove XML attribute. This is a special case of add
  attribute.

* `fix().url`: Show URL. In some cases, you can't “fix” or do anything
  local to address the problem, but you really want to direct the
  user's attention to additional documentation. In that case, you can
  attach a “show this URL” quick fix to the incident which will open
  the browser with the given URL when invoked. For example, in a
  complicated deprecation where you want users to migrate from one
  approach to a completely different one that you cannot automate, you
  could use something like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin
val message = "Job scheduling with `GcmNetworkManager` is deprecated: Use AndroidX `WorkManager` instead"
val fix = fix()
.url("https://developer.android.com/topic/libraries/architecture/workmanager/migrating-gcm")
.build()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Combining Fixes

You might notice that lint's APIs to report incidents only takes a
**single** quick fix instead of a list of fixes.

But let's say that it *did* take a list of quick fixes.

- Should they *all* be performed as a single unit? That makes sense if
  you're trying to write a quickfix which performs multiple string
  replacements.

- Or should they be offered as separate alternatives for the user to
  choose between? That makes sense if the incident says for example
  that you must set at least one attribute among three possibilities;
  in this case we may want to add quickfixes for setting each attribute.

Both scenarios have their uses, so lint makes this explicit:

- `fix().composite`: create a “composite” fix, which composes the fix
  out of multiple individual fixes, or

- `fix().alternatives`: create an “alternatives” fix, which holds a
  number of individual fixes, which lint will present as separate
  options to the user.

Here's an example of how to create a composite fix, which will be
performed as a unit; here we're both setting a new attribute and
deleting a previous attribute:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin
val fix = fix().name("Replace with singleLine=\"true\"")
    .composite(
        fix().set(ANDROID_URI, "singleLine", "true").build(),
        fix().unset(namespace, oldAttributeName).build()
    )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And here's an example of how to create an alternatives fix, which are
offered to the user as separate options; this is from our earlier
example of the accessibility check which requires you to set a content
description, which can be set either on the “text” attribute or the
“contentDescription” attribute:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin
val fix = fix().alternatives(
    fix().set().todo(ANDROID_URI, "text").build(),
    fix().set().todo(ANDROID_URI, "contentDescription")
    .build())
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Refactoring Java and Kotlin code

It would be nice if there was an AST manipulation API, similar to UAST
for visiting ASTs, that quickfixes could use to implement refactorings,
but we don't have a library like that. And it's unlikely it would work
well; when you rewrite the user's code you typically have to take
language specific conventions into account.

Therefore, today, when you create quickfixes for Kotlin and Java code,
if the quickfix isn't something simple which would work for both
languages, then you need to conditionally create either the Kotlin
version or the Java version of the quickfix based on whether the source
file it applies to is in Kotlin or Java. (For an easy way to check you
can use the `isKotlin` or `isJava` package level methods in
`com.android.tools.lint.detector.api`.)

However, it's often the case that the quickfix is something simple
which would work for both; that's true for most of the built-in lint
checks with quickfixes for Kotlin and Java.

## Regular Expressions and Back References

The `replace` string quick fix allows you to match the text to
with regular expressions.

You can also use back references in the regular expression such
that the quick fix replacement text includes portions from the
original string.

Here's an example from lint's `AssertDetector`:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers
val fix = fix().name("Surround with desiredAssertionStatus() check")
    .replace()
    .range(context.getLocation(assertCall))
    .pattern("(.*)")
    .with("if (javaClass.desiredAssertionStatus()) { \\k<1> }")
    .reformat(true)
    .build()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The replacement string's back reference above, on line 5, is \k<1>. If
there were multiple regular expression groups in the replacement
string, this could have been \k<2>, \k<3>, and so on.

Here's how this looks when applied, from its unit test:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin
lint().files().run().expectFixDiffs(
    """
    Fix for src/test/pkg/AssertTest.kt line 18: Surround with desiredAssertionStatus() check:
    @@ -18 +18
    -         assert(expensive()) // WARN
    +         if (javaClass.desiredAssertionStatus()) { assert(expensive()) } // WARN
    """
)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Emitting quick fix XML to apply on CI

Note that the `lint` has an option (`--describe-suggestions`) to emit
an XML file which describes all the edits to perform on documents to
apply a fix. This maps all quick fixes into chapter edits (including
XML logic operations). This can be (and is, within Google) used to
integrate with code review tools such that the user can choose whether
to auto-fix a suggestion right from within the code review tool.

<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>
